[项目系列文章说明]: 该类型的文章是对项目的实现方案和部分代码进行说明.
除了使用Spring cache之外, 项目对自定义缓存扩展有一定需求的情况下, 自己动手编写一个针对方法级别的缓存(可扩展支持到类,但不易维护, 不建议使用类级别的).
结合STC-stcconfig动态配置系统, 可实现缓存的动态删除, 缓存开关实时生效.
代码地址: https://github.com/lvxingzhi/stcdatacache
系统实现原理
利用Spring 提供的AOP机制,实现对方法的扩展.
利用Spel表达式实现方法入参与缓存key动态匹配.
预定义缓存接口,实现对不同缓存方式的兼容.
异常处理保证业务的可用性.
整理并不复杂, 流程图略.
依赖
1, Spring
2, 任意分布式缓存
目录结构
annotation: 注解
aspect: 注解处理器
cache: 缓存接口定义和本地实现案例
test: 测试Demo
核心代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
| /** * STCDataCache处理器 * * @Author xingzhi.lv * @Version 2.1 * @Date 2021/11/3 11:02 */ @Aspect @Component public class STCDataCacheAspect { private static final Logger logger = LoggerFactory.getLogger(STCDataCacheAspect.class); public static final ExpressionParser EXPRESSION_PARSER = new SpelExpressionParser(); public static final String KEY_SPLIT = "_"; private STCCacheTemplate cacheTemplate = new STCCacheLocalTemplate(); public static final String STCNULL = "STCNULL"; // 缓存切面 @Pointcut("@annotation(com.note.stcdatacache.annotation.STCDataCache)") public void methodCachePoint() { } // 删除缓存切面 @Pointcut("@annotation(com.note.stcdatacache.annotation.STCDataCacheDelete)") public void methodCacheDeletePoint() { }
@Around(value = "methodCachePoint()") public Object around(ProceedingJoinPoint joinPoint) throws Throwable { try { MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); STCDataCache cacheAnno = AnnotationUtils.findAnnotation(method, STCDataCache.class); String keyEL = cacheAnno.cacheKey(); Object[] args = joinPoint.getArgs(); String[] argNames = signature.getParameterNames(); // 使用Spel解析 EvaluationContext context = new StandardEvaluationContext(); String cacheKey; if (Strings.isNotEmpty(keyEL)) { if (args != null && args.length > 0) { for (int i = 0; i < argNames.length; i++) { context.setVariable(argNames[i], args[i]); } } Object obj = EXPRESSION_PARSER.parseExpression(keyEL).getValue(context); cacheKey = obj.toString(); } else { String className = joinPoint.getSignature().getDeclaringTypeName(); String methodName = joinPoint.getSignature().getName(); cacheKey = className + KEY_SPLIT + methodName; } String group = cacheAnno.group(); cacheKey = group + KEY_SPLIT + cacheKey; // 读取缓存数据 Object cacheResult = cacheTemplate.get(cacheKey); if (cacheResult != null && !STCNULL.equals(cacheResult)) { return cacheResult; } // 空占位符 if (STCNULL.equals(cacheResult)) { return null; } final Object result = joinPoint.proceed(); if (Objects.isNull(result)) { cacheTemplate.set(cacheKey, STCNULL); } else { cacheTemplate.set(cacheKey, result); } return result; } catch (Throwable ta) { logger.error("STCDataCache read cache error", ta); return joinPoint.proceed(); } }
@Around(value = "methodCacheDeletePoint()") public Object deleteAround(ProceedingJoinPoint joinPoint) throws Throwable { try { MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); STCDataCacheDelete cacheAnno = AnnotationUtils.findAnnotation(method, STCDataCacheDelete.class); String keyEL = cacheAnno.cacheKey(); Object[] args = joinPoint.getArgs(); String[] argNames = signature.getParameterNames(); // 使用Spel解析 EvaluationContext context = new StandardEvaluationContext(); String cacheKey; if (Strings.isNotEmpty(keyEL)) { if (args != null && args.length > 0) { for (int i = 0; i < argNames.length; i++) { context.setVariable(argNames[i], args[i]); } } Object obj = EXPRESSION_PARSER.parseExpression(keyEL).getValue(context); cacheKey = obj.toString(); } else { String className = joinPoint.getSignature().getDeclaringTypeName(); String methodName = joinPoint.getSignature().getName(); cacheKey = className + KEY_SPLIT + methodName; } String group = cacheAnno.group(); cacheKey = group + KEY_SPLIT + cacheKey; // 删除缓存数据 cacheTemplate.delete(cacheKey); } catch (Throwable ta) { logger.error("STCDataCache delete cache error", ta); } return joinPoint.proceed(); }
public STCCacheTemplate getCacheTemplate() { return cacheTemplate; }
public void setCacheTemplate(STCCacheTemplate cacheTemplate) { this.cacheTemplate = cacheTemplate; }
|
测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| @SpringBootTest class StcdatacacheApplicationTests {
@Resource private DemoService demoService;
@Test void contextLoads() { String demoName1 = demoService.findDemoName(58); System.out.println(demoName1); String demoName2 = demoService.findDemoName(58); System.out.println(demoName2); demoService.deleteDemoDelete(58); String demoName3 = demoService.findDemoName(58); System.out.println(demoName3); }
}
|
结果
1 2 3
| DemoName : -2094750780 DemoName : -2094750780 DemoName : 336840328
|